/*******************************************************************************
 *  Copyright (c) 2005, 2015 IBM Corporation and others.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 * 
 *  Contributors:
 *     IBM Corporation - initial API and implementation
 *     Bjorn Freeman-Benson - initial API and implementation
 *     Wind River Systems - adopted to use with DSF
 *******************************************************************************/
package org.eclipse.cdt.examples.dsf.pda.ui.breakpoints;

import org.eclipse.cdt.examples.dsf.pda.PDAPlugin;
import org.eclipse.cdt.examples.dsf.pda.breakpoints.PDALineBreakpoint;
import org.eclipse.cdt.examples.dsf.pda.breakpoints.PDAWatchpoint;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.ILineBreakpoint;
import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;


/**
 * Adapter to create breakpoints in PDA files.
 * <p>
 * This class is identical to the corresponding in PDA debugger implemented in 
 * org.eclipse.debug.examples.ui.
 * </p>
 */
public class PDABreakpointAdapter implements IToggleBreakpointsTargetExtension {
	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleLineBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException {
		ITextEditor textEditor = getEditor(part);
		if (textEditor != null) {
			IResource resource = textEditor.getEditorInput().getAdapter(IResource.class);
			ITextSelection textSelection = (ITextSelection) selection;
			int lineNumber = textSelection.getStartLine();
			IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PDAPlugin.ID_PDA_DEBUG_MODEL);
			for (int i = 0; i < breakpoints.length; i++) {
				IBreakpoint breakpoint = breakpoints[i];
				if (breakpoint instanceof ILineBreakpoint && resource.equals(breakpoint.getMarker().getResource())) {
					if (((ILineBreakpoint)breakpoint).getLineNumber() == (lineNumber + 1)) {
						// remove
						breakpoint.delete();
						return;
					}
				}
			}
			// create line breakpoint (doc line numbers start at 0)
			PDALineBreakpoint lineBreakpoint = new PDALineBreakpoint(resource, lineNumber + 1);
			DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(lineBreakpoint);
		}
	}
	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleLineBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) {
		return getEditor(part) != null;
	}
	
	/**
	 * Returns the editor being used to edit a PDA file, associated with the
	 * given part, or <code>null</code> if none.
	 *  
	 * @param part workbench part
	 * @return the editor being used to edit a PDA file, associated with the
	 * given part, or <code>null</code> if none
	 */
	private ITextEditor getEditor(IWorkbenchPart part) {
		if (part instanceof ITextEditor) {
			ITextEditor editorPart = (ITextEditor) part;
			IResource resource = editorPart.getEditorInput().getAdapter(IResource.class);
			if (resource != null) {
				String extension = resource.getFileExtension();
				if (extension != null && extension.equals("pda")) {
					return editorPart;
				}
			}
		}
		return null;		
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleMethodBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	public void toggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException {
	}
	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleMethodBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) {
		return false;
	}
	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleWatchpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	public void toggleWatchpoints(IWorkbenchPart part, ISelection selection) throws CoreException {
	    String[] variableAndFunctionName = getVariableAndFunctionName(part, selection);
	    if (variableAndFunctionName != null && part instanceof ITextEditor && selection instanceof ITextSelection) {
	        ITextEditor editorPart = (ITextEditor)part;
	        int lineNumber = ((ITextSelection)selection).getStartLine();
	        IResource resource = editorPart.getEditorInput().getAdapter(IResource.class);
	        String var = variableAndFunctionName[0];
	        String fcn = variableAndFunctionName[1];
	        // look for existing watchpoint to delete
			IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(PDAPlugin.ID_PDA_DEBUG_MODEL);
			for (int i = 0; i < breakpoints.length; i++) {
				IBreakpoint breakpoint = breakpoints[i];
				if (breakpoint instanceof PDAWatchpoint && resource.equals(breakpoint.getMarker().getResource())) {
				    PDAWatchpoint watchpoint = (PDAWatchpoint)breakpoint;
				    String otherVar = watchpoint.getVariableName();
				    String otherFcn = watchpoint.getFunctionName();
					if (otherVar.equals(var) && otherFcn.equals(fcn)) {
						breakpoint.delete();
						return;
					}
				}
			}
			// create watchpoint
			PDAWatchpoint watchpoint = new PDAWatchpoint(resource, lineNumber + 1, fcn, var, true, true);
			DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(watchpoint);	        
	    }
	}
	/* (non-Javadoc)
	 * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleWatchpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) {
		return getVariableAndFunctionName(part, selection) != null;
	}
	
	/**
	 * Returns the variable and function names at the current line, or <code>null</code> if none.
	 * 
	 * @param part text editor
	 * @param selection text selection
	 * @return the variable and function names at the current line, or <code>null</code> if none.
	 *  The array has two elements, the first is the variable name, the second is the function name.
	 */
	private String[] getVariableAndFunctionName(IWorkbenchPart part, ISelection selection) {
	    ITextEditor editor = getEditor(part);
	    if (editor != null && selection instanceof ITextSelection) {
	        ITextSelection textSelection = (ITextSelection) selection;
	        IDocumentProvider documentProvider = editor.getDocumentProvider();
	        try {
	            documentProvider.connect(this);
	            IDocument document = documentProvider.getDocument(editor.getEditorInput());
	            IRegion region = document.getLineInformationOfOffset(textSelection.getOffset());
	            String string = document.get(region.getOffset(), region.getLength()).trim();
	            if (string.startsWith("var ")) {
	                String varName = string.substring(4).trim(); 
	                String fcnName = getFunctionName(document, varName, document.getLineOfOffset(textSelection.getOffset()));
	                return new String[] {varName, fcnName};
	            }
	        } catch (CoreException e) {
	        } catch (BadLocationException e) {
	        } finally {
	            documentProvider.disconnect(this);
	        }
	    }	    
	    return null;
	}
	
	/**
	 * Returns the name of the function containing the given variable defined at the given
	 * line number in the specified document.
	 * 
	 * @param document PDA source file
	 * @param varName variable name
	 * @param line line numbner at which the variable is defined
	 * @return name of function defining the variable
	 */
	private String getFunctionName(IDocument document, String varName, int line) {
	    // This is a simple guess at the function name - look for the labels preceeding
	    // the variable definition, and then see if there are any 'calls' to that
	    // label. If none, assumet the variable is in the "_main_" function
	    String source = document.get();
	    int lineIndex = line - 1;
	    while (lineIndex >= 0) {
            try {
                IRegion information = document.getLineInformation(lineIndex);
                String lineText = document.get(information.getOffset(), information.getLength());
                if (lineText.startsWith(":")) {
                    String label = lineText.substring(1);
                    if (source.indexOf("call " + label) >= 0) {
                        return label;
                    }
                }
                lineIndex--;
            } catch (BadLocationException e) {
            }
	    }
	    return "_main_";
	}
    
    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension#toggleBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
     */
    public void toggleBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException {
        if (canToggleWatchpoints(part, selection)) {
            toggleWatchpoints(part, selection);
        } else {
            toggleLineBreakpoints(part, selection);
        }    
    }
    
    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTargetExtension#canToggleBreakpoints(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
     */
    public boolean canToggleBreakpoints(IWorkbenchPart part, ISelection selection) {
        return canToggleLineBreakpoints(part, selection) || canToggleWatchpoints(part, selection);
    }
}
